﻿using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using FFmpegVideoClip.Lib;
using FFmpegVideoClip.Lib.Controls;
using Microsoft.Win32;
using Unosquare.FFME.Common;
using MediaElement = Unosquare.FFME.MediaElement;
#pragma warning disable CA1416 // 验证平台兼容性
namespace FFmpegVideoClip
{
    public class MediaElementConcrol: INotifyPropertyChanged
    {
        public static MediaElementConcrol? CurrentInstance;
        public event PropertyChangedEventHandler? PropertyChanged;
        public void SendNotifyPropertyChanged(string PropertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName)); }

        public static string? LastPath,OpenintPath;
        private static MainWindow? mainWindow;

        private bool IsSmallChangeEndState = false, IsEnterKey = false, IsReOpen = false;
        private MediaElement Me;
        private Slider m_ProgressBar,m_mSlider,m_HmSlider;
        private Button PB, CB;
        private TimeSpan MouseDownTime;
        private Action? m_OnLoaded;
        private TimeSpan RePlayRecordTime;
        public bool HasVideo => Me.HasVideo;
        public int VideoWidth => IsOpen ? Me.NaturalVideoWidth : 0;
        public int VideoHeight => IsOpen ? Me.NaturalVideoHeight : 0;
        public string VideoAspectRatio => IsOpen ? Me.VideoAspectRatio : string.Empty;
        public double VideoAspectRatioValue => VideoWidth / (double)VideoHeight;
        public bool IsOpen => PB.IsEnabled;
        public double Volume => Me.Volume;
        public bool ScrubbingEnabled
        {
            get => Me.ScrubbingEnabled;
            set
            {
                if (Me.ScrubbingEnabled == value)
                    return;
                Me.ScrubbingEnabled = value;
                SendNotifyPropertyChanged(nameof(ScrubbingEnabled));
            }
        }
        public bool VerticalSyncEnabled
        {
            get=>Me.VerticalSyncEnabled;
            set
            {
                if(Me.VerticalSyncEnabled == value)
                    return;
                Me.VerticalSyncEnabled = value;
                SendNotifyPropertyChanged(nameof(VerticalSyncEnabled));
            }
        }
        public TimeSpan? MediaDuration => Me.NaturalDuration;
        public TimeSpan CurrentTimePosition { get => Me.Position; set => Me.Position = value; }
        public Uri? CurrentMediaPathUri => Me.Source;
        public string CurrentMediaPath => Me.Source == null ? string.Empty : HttpUtility.UrlDecode(Me.Source.AbsolutePath).Replace('/', '\\');
        public string CurrentMediaName => Me.Source == null ? string.Empty : Path.GetFileNameWithoutExtension(CurrentMediaPath);
        public string CurrentMediaFileName => Me.Source == null ? string.Empty : Path.GetFileName(CurrentMediaPath);
        public string VoluemText => $"🎶🔊:{(int)Math.Round(Me.Volume * 100, MidpointRounding.AwayFromZero)}%";
        public string PlaySpeedText => $"⏭:{(int)Math.Round(Me.SpeedRatio * 100, MidpointRounding.AwayFromZero)}%";
        public MediaElementConcrol(MainWindow owner,[DisallowNull] MediaElement mediaElement, 
            [DisallowNull] Slider ProgressBar, [DisallowNull] Slider mSlider, [DisallowNull] Slider HmSlider, [DisallowNull] TimeLineSlider timeLine,
            [DisallowNull] Button PlayButton, [DisallowNull] Button CloseButton)
        {
            if(mediaElement is null)
                throw new ArgumentNullException(nameof(mediaElement), $"{nameof(mediaElement)}不允许Null");
            if(ProgressBar is null)
                throw new ArgumentNullException(nameof(ProgressBar), $"{nameof(ProgressBar)}不允许Null");
            if (mSlider is null)
                throw new ArgumentNullException(nameof(mSlider), $"{nameof(mSlider)}不允许Null");
            if (HmSlider is null)
                throw new ArgumentNullException(nameof(HmSlider), $"{nameof(HmSlider)}不允许Null");
            if (PlayButton is null)
                throw new ArgumentNullException(nameof(PlayButton), $"{nameof(PlayButton)}不允许Null");
            if (CloseButton is null)
                throw new ArgumentNullException(nameof(CloseButton), $"{nameof(CloseButton)}不允许Null");
            
            CurrentInstance = this;
            mainWindow = owner;

            Me = mediaElement;
            Me.MediaEnded += Me_MediaEnded;
            Me.MediaOpened += LoadMediaOK;
            Me.MediaFailed += Me_MediaFailed;
            Me.MediaStateChanged += MediaStateChanged;
            ClipManage.CurrentMediaElement = Me;

            m_ProgressBar = ProgressBar;
            m_mSlider = mSlider;
            m_HmSlider = HmSlider;

            m_mSlider.PreviewMouseLeftButtonDown += SliderMouseDown;
            m_mSlider.PreviewMouseLeftButtonUp += SliderMouseUp;
            m_HmSlider.PreviewMouseLeftButtonDown += SliderMouseDown;
            m_HmSlider.PreviewMouseLeftButtonUp += SliderMouseUp;

            m_mSlider.ValueChanged += M_mSlider_ValueChanged;
            m_mSlider.Loaded += (s,e)=> SetSliderThumbEvent(m_mSlider);
            m_HmSlider.ValueChanged += M_HmSlider_ValueChanged;
            m_HmSlider.Loaded += (s, e) => SetSliderThumbEvent(m_HmSlider);


            PB = PlayButton;
            CB = CloseButton;
            PB.IsEnabled = false;
            CB.IsEnabled = true;
            PB.Click += PauseOrPlayer_PB_Click;
            CB.Click += OpenOrClose_CB_Click;

            mainWindow.PreviewKeyDown += Owner_PreviewKeyDown;
            owner.FragmentName.KeyDown += FragmentName_KeyDown;
            Me.MouseDown+=(s,e)=> PauseOrPlayer_PB_Click(null!, null!);

            //Binding VolumeSliderBinding = new Binding();
            //VolumeSliderBinding.Source= mainWindow.VolumeSlider;
            //VolumeSliderBinding.Path = new PropertyPath("Value");

            DependencyPropertyDescriptor.FromProperty(MediaElement.VolumeProperty, typeof(MediaElement))
                .AddValueChanged(Me, (s, e) => { SendNotifyPropertyChanged(nameof(VoluemText)); mainWindow.VoluemTextShow.IsShowStateAnim = true; VoluemTextHideTaskCall(); });
            DependencyPropertyDescriptor.FromProperty(MediaElement.SpeedRatioProperty, typeof(MediaElement))
                .AddValueChanged(Me, (s, e) => { SendNotifyPropertyChanged(nameof(PlaySpeedText)); mainWindow.PlaySpeedTextShow.IsShowStateAnim = true; PlaySpeedTextHideTaskCall(); });

            VerticalSyncEnabled = false;
            ScrubbingEnabled = false;
        }

        private Task? VoluemTextHideTask, PlaySpeedTextHideTask;
        private TimeSpan VoluemTextHideTime, PlaySpeedTextHideTime;
        private void VoluemTextHideTaskCall()
        {
            VoluemTextHideTime = TimeSpan.FromSeconds(3);
            if (VoluemTextHideTask == null)
            {
                VoluemTextHideTask = Task.Run(() =>
                {
                    while (VoluemTextHideTime > TimeSpan.Zero)
                    {
                        VoluemTextHideTime = VoluemTextHideTime.Subtract(Tools.OneSecond);
                        Thread.Sleep(1000);
                    }
                    Application.Current.Dispatcher.BeginInvoke(() => mainWindow!.VoluemTextShow.IsShowStateAnim = false);
                    VoluemTextHideTask = null;
                });
            }
        }
        private void PlaySpeedTextHideTaskCall()
        {
            PlaySpeedTextHideTime = TimeSpan.FromSeconds(3);
            if (PlaySpeedTextHideTask == null)
            {
                PlaySpeedTextHideTask = Task.Run(() =>
                {
                    while (PlaySpeedTextHideTime > TimeSpan.Zero)
                    {
                        PlaySpeedTextHideTime = PlaySpeedTextHideTime.Subtract(Tools.OneSecond);
                        Thread.Sleep(1000);
                    }
                    Application.Current.Dispatcher.BeginInvoke(() => mainWindow!.PlaySpeedTextShow.IsShowStateAnim = false);
                    PlaySpeedTextHideTask = null;
                });
            }
        }
       

        #region 设置鼠标拖动微调完成后的位置
        private Point? MouseDownPoint;
        private void SetSliderThumbEvent(Slider slider)
        {
            Thumb? thumb = Tools.GetChilds<Thumb>(slider).FirstOrDefault();
            if (thumb == null)
            {
                LogManage.WriteLineNewInfo($"微调模式控件[{slider.Name}]鼠标拖动完成事件设置失败");
                return;
            }
            thumb!.DragStarted += Thumb_DragStarted;
            thumb!.DragCompleted += MediaElementConcrol_DragCompleted;
        }
        private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
        {
            FrameworkElement fe = (FrameworkElement)sender;
            MouseDownPoint = fe.PointToScreen(Mouse.GetPosition(fe));
        }
        private void MediaElementConcrol_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            if (!MouseDownPoint.HasValue)
                return;
            Tools.SetCursorPos(MouseDownPoint.Value.X, MouseDownPoint.Value.Y);
            MouseDownPoint = null;
        }
        #endregion

        //处理片段名称控件的焦点
        private void FragmentName_KeyDown(object sender, KeyEventArgs e)
        {
            if(e.Key == Key.Enter || e.Key==Key.Escape)
                mainWindow!.StartTimeText.Focus();
        }

        /// <summary>
        /// 空格暂停播放
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Owner_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (sender is not MainWindow mw)
                return;
            //过滤文本输入空间
            if (Keyboard.FocusedElement is TextBox tb && !(tb == mw.StartTimeText || tb == mw.EndTimeText) && !tb.IsReadOnly)
                return;
            if (e.Key == Key.Space || e.Key == Key.Enter)
            {
                if (e.Key == Key.Enter)
                    IsEnterKey = true;
                else
                    IsEnterKey = false;
                PauseOrPlayer_PB_Click(null!, null!);
            }
        }

        public async Task OpenMedia(string MediaPath, Action? OnLoaded = null)
        {
            mainWindow!.StatusInfo.Visibility = Visibility.Collapsed;
            LastPath = MediaPath;
            m_OnLoaded = null;
            if (OpenintPath == null || !OpenintPath.Equals(MediaPath, StringComparison.OrdinalIgnoreCase))
            {
                PB.IsEnabled = false;
                OpenintPath = MediaPath;
                if (Me.LoadedBehavior != MediaPlaybackState.Manual)
                    Me.LoadedBehavior = MediaPlaybackState.Manual;
                m_OnLoaded = OnLoaded;
                await ClearTimeLineTickMarks();
                await Me.Open(new Uri(MediaPath));
            }
        }
        private void LoadMediaOK(object? sender,MediaOpenedEventArgs e)
        {
            DateTime recordTime = DateTime.Now;
            PB.IsEnabled = true;
            m_ProgressBar.Maximum = Me.NaturalDuration == TimeSpan.Zero ? 1 : Me.NaturalDuration!.Value.TotalMilliseconds;
            if (!IsReOpen)
                mainWindow!.TimeLine.FilePath = CurrentMediaPath;
            m_OnLoaded?.Invoke();
            m_OnLoaded = null;
            if (IsReOpen)
            {
                Me.Position = MouseDownTime;
                IsReOpen = false;
            }
        }
        private void Me_MediaFailed(object? sender, MediaFailedEventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            Exception? exception = e.ErrorException;
            sb.Append(exception.Message);
            exception = exception.InnerException;
            while (exception != null)
            {
                sb.Append($"--> {exception.Message}");
                exception = exception.InnerException;
            }
            mainWindow!.StatusInfo.Visibility = Visibility.Visible;
            mainWindow.StatusInfo.Text = $"打开文件失败：{sb.ToString()}";
            LogManage.WriteExceptionInfo($"打开文件 [{OpenintPath}] 失败：", e.ErrorException, true);
            PB.IsEnabled = false;
            OpenintPath = null;
        }
        private void MediaStateChanged(object? sender, MediaStateChangedEventArgs e)
        {
            switch (e.MediaState)
            {
                case MediaPlaybackState.Play:
                    PB.Content = "‖";
                    CB.Content = "✖";
                    return;
                case MediaPlaybackState.Close:
                    PB.Content = "▶";
                    CB.Content = "⏏";
                    return;
                case MediaPlaybackState.Pause:
                    PB.Content = "▶";
                    CB.Content = "✖";
                    PauseReturn();
                    return;
                case MediaPlaybackState.Stop:
                    PB.Content = "▶";
                    CB.Content = "✖";
                    return;
                case MediaPlaybackState.Manual:
                    PB.Content = "▶";
                    CB.Content = "✖";
                    return;
                default:
                    PB.Content = "▶";
                    CB.Content = "⏏";
                    return;
            }
        }
        private void Me_MediaEnded(object? sender, EventArgs e) => Pause();
    
        
        /// <summary>
        /// 微调-当鼠标按下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SliderMouseDown(object sender, MouseButtonEventArgs e) { MouseDownTime = Me.Position;  }

        /// <summary>
        /// 微调-当鼠标抬起
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SliderMouseUp(object sender, MouseButtonEventArgs e)
        {
            Slider m_Slider = (Slider)sender;
            IsSmallChangeEndState = true;
            m_Slider.Value = 0;
            IsSmallChangeEndState = false;
        }

        /// <summary>
        /// 微调-秒
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void M_mSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        { if (!IsSmallChangeEndState) m_ProgressBar.Value = (MouseDownTime + TimeSpan.FromSeconds(m_mSlider.Value)).TotalMilliseconds; }

        /// <summary>
        /// 微调-毫秒
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void M_HmSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        { if (!IsSmallChangeEndState) m_ProgressBar.Value = (MouseDownTime + TimeSpan.FromMilliseconds(m_HmSlider.Value)).TotalMilliseconds; }

        /// <summary>
        /// 暂停和播放
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PauseOrPlayer_PB_Click(object sender, RoutedEventArgs e)
        {
            if (Me.MediaState ==  MediaPlaybackState.Play)
                Pause();
            else if (Me.MediaState == MediaPlaybackState.Pause || Me.MediaState == MediaPlaybackState.Stop)
                Play();
        }

        /// <summary>
        /// 媒体-关闭和打开
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void OpenOrClose_CB_Click(object sender, RoutedEventArgs e)
        {
            if (Me.MediaState == MediaPlaybackState.Close)
            {
                if (OpenPath(out string SelectFilePath))

                await OpenMedia(SelectFilePath, () => mainWindow!.SetCurrentMediaInfo_Click(null!, null!));
            }
            else
               await Close();
        }

        private async Task ClearTimeLineTickMarks()
        {
            if (!IsReOpen)
                await mainWindow!.TimeLine.Clear();
        }

        /// <summary>
        /// 进度时间点返回
        /// </summary>
        private void PauseReturn()
        {
            if (!IsEnterKey && mainWindow!.ReTime.IsChecked!.Value)
                CurrentTimePosition = RePlayRecordTime;
        }

        public void Play()
        {
            if (Me.MediaState == MediaPlaybackState.Play || Me.MediaState == MediaPlaybackState.Close)
                return;
            RePlayRecordTime = CurrentTimePosition;
            Me.Play();
        }
        public void Pause()
        {
            if (Me.MediaState != MediaPlaybackState.Play)
                return;
            Me.Pause();
        }

        public async Task Close()
        {
            if (Me.MediaState == MediaPlaybackState.Close)
                return;

            await Me.Close();

            OpenintPath = null;
            if (!IsReOpen)
                mainWindow!.TimeLine.FilePath = string.Empty;
            m_ProgressBar.Value = 0;
            PB.IsEnabled = false;
            await ClearTimeLineTickMarks();
        }

        public void ReOpen()
        {
            MouseDownTime = Me.Position;
            Uri curr = new Uri(OpenintPath!);
            IsReOpen = true;
            Me.Close();
            Me.Open(curr);
            
        }

        /// <summary>
        /// 打开文件对话
        /// </summary>
        /// <param name="Path"></param>
        /// <returns></returns>
        public static bool OpenPath(out string Path)
        {
            Path = string.Empty;
            OpenFileDialog ofd = new();
            ofd.Filter = "全部|*.*";
            ofd.Title = "打开媒体文件";
            ofd.CheckFileExists = true;
            ofd.DereferenceLinks = true;
            ofd.InitialDirectory = !string.IsNullOrEmpty(LastPath) && Directory.Exists(LastPath) ? LastPath : Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
            bool? result = ofd.ShowDialog(mainWindow);
            if (result.HasValue && result.Value)
            {
                Path = ofd.FileName;
                return true;
            }
            return false;
        }
    }
}
#pragma warning restore CA1416 // 验证平台兼容性